<!doctype html>



  


<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>






<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Android,View,Resources,换肤," />








  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />






<meta name="description" content="本文主要是在公司内部的一次分享会内容，基于 Android－Skin－Loader开源库，实现一套完整的在线换肤方案。同时，也参考了很多网上总结的换肤思路，后面会给出主要的参考资料。">
<meta name="keywords" content="Android,View,Resources,换肤">
<meta property="og:type" content="article">
<meta property="og:title" content="Android 在线换肤方案总结分享">
<meta property="og:url" content="http://imesong.com/2016/04/05/2016/Android-在线换肤方案总结分享/index.html">
<meta property="og:site_name" content="imesong">
<meta property="og:description" content="本文主要是在公司内部的一次分享会内容，基于 Android－Skin－Loader开源库，实现一套完整的在线换肤方案。同时，也参考了很多网上总结的换肤思路，后面会给出主要的参考资料。">
<meta property="og:image" content="http://7xrkqt.com1.z0.glb.clouddn.com/image%2F2016%2Fthemedemo.gif">
<meta property="og:image" content="http://androiddoc.qiniudn.com/images/build.png">
<meta property="og:image" content="http://7xrkqt.com1.z0.glb.clouddn.com/blogimage/2016/QQ20160406-0.png-blogpic">
<meta property="og:updated_time" content="2017-04-19T04:36:26.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Android 在线换肤方案总结分享">
<meta name="twitter:description" content="本文主要是在公司内部的一次分享会内容，基于 Android－Skin－Loader开源库，实现一套完整的在线换肤方案。同时，也参考了很多网上总结的换肤思路，后面会给出主要的参考资料。">
<meta name="twitter:image" content="http://7xrkqt.com1.z0.glb.clouddn.com/image%2F2016%2Fthemedemo.gif">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Pisces',
    sidebar: {"position":"left","display":"post","offset":12,"offset_float":0,"b2t":false,"scrollpercent":false},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://imesong.com/2016/04/05/2016/Android-在线换肤方案总结分享/"/>





  <title> Android 在线换肤方案总结分享 | imesong </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?cb36183ed65a7652b909d352ab2166";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>










  
  
    
  

  <div class="container sidebar-position-left page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">imesong</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">我们的征途是星辰大海</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <link itemprop="mainEntityOfPage" href="http://imesong.com/2016/04/05/2016/Android-在线换肤方案总结分享/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="imesong">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="https://s.gravatar.com/avatar/bd2525e0ecc6d8022339c7dce27065c0?s=160">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="imesong">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                Android 在线换肤方案总结分享
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2016-04-05T16:48:52+08:00">
                2016-04-05
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index">
                    <span itemprop="name">Android</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    


    <div class="post-body han-init-context" itemprop="articleBody">

      
      

      
        <blockquote>
<p>本文主要是在公司内部的一次分享会内容，基于 Android－Skin－Loader开源库，实现一套完整的在线换肤方案。同时，也参考了很多网上总结的换肤思路，后面会给出主要的参考资料。</p>
</blockquote>
<a id="more"></a>
<p><strong>我们先看下Demo效果图</strong></p>
<p><img src="http://7xrkqt.com1.z0.glb.clouddn.com/image%2F2016%2Fthemedemo.gif" alt="Demo效果图"></p>
<h3 id="为什么要做在线换肤？"><a href="#为什么要做在线换肤？" class="headerlink" title="为什么要做在线换肤？"></a>为什么要做在线换肤？</h3><h4 id="皮肤模块独立，减小-Apk-包大小"><a href="#皮肤模块独立，减小-Apk-包大小" class="headerlink" title="皮肤模块独立，减小 Apk 包大小"></a>皮肤模块独立，减小 Apk 包大小</h4><p>资源文件在App大小中有很大的比重，特别是工具类的App，用户有使用多套皮肤的需求，如果多套皮肤资源全部在 App 中，会极大的增加 App 的大小，不利于用户下载以及渠道推广。</p>
<h4 id="技术方案通用，不局限于具体的-App"><a href="#技术方案通用，不局限于具体的-App" class="headerlink" title="技术方案通用，不局限于具体的 App"></a>技术方案通用，不局限于具体的 App</h4><p>现在公司有十几款移动端产品，但是还没有App使用在线换肤或者使用类似插件式的换肤方案，如果能调研出一套完整的在线换肤方案，公司的各个 App 都可以使用。</p>
<h4 id="服务端控制皮肤包，动态更新，满足运营需求"><a href="#服务端控制皮肤包，动态更新，满足运营需求" class="headerlink" title="服务端控制皮肤包，动态更新，满足运营需求"></a>服务端控制皮肤包，动态更新，满足运营需求</h4><p>皮肤包资源放在服务端，动态更新，可以满足运营需求，发布不同主题皮肤资源包，避免通过发布版本迭代更新。</p>
<h4 id="降低维护成本"><a href="#降低维护成本" class="headerlink" title="降低维护成本"></a>降低维护成本</h4><p>通过把皮肤模块独立出来，减少主工程的逻辑，精简主工程代码，降低维护成本。</p>
<h3 id="在线换肤的难点在哪里？"><a href="#在线换肤的难点在哪里？" class="headerlink" title="在线换肤的难点在哪里？"></a>在线换肤的难点在哪里？</h3><p>调研一个技术方案，有难点，有重点。当我们把这些重点难点攻克，方案的雏形基本就完成了。</p>
<h4 id="如何加载皮肤资源文件"><a href="#如何加载皮肤资源文件" class="headerlink" title="如何加载皮肤资源文件"></a>如何加载皮肤资源文件</h4><p>在线换肤，皮肤资源肯定不会在Apk内部，要怎么加载外部的皮肤资源呢？下载到本地，如何加载外部的资源文件呢？<br>我们先看下 Apk 的打包流程。</p>
<p><img src="http://androiddoc.qiniudn.com/images/build.png" alt="Android打包流程"></p>
<p>这里流程中，有两个关键点<br><strong>1.R文件的生成</strong><br>R文件是一个Java文件，通过R文件我们就可以找到对应的资源。R文件就像一张映射表，帮助我们找到资源文件。<br><strong>2.资源文件的打包生成</strong></p>
<p>资源文件经过压缩打包，生成 resources 文件，通过R文件找到里面保存的对映的资源文件<br>在 App 内部，我们一般通过下面代码，获取资源</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">context.getResource.getString(R.string.hello);</div><div class="line">context.getResource.getColor(R.color.black);</div><div class="line">context.getResource.getDrawable(R.drawable.splash);</div></pre></td></tr></table></figure>
<p>这个时获取 App 内部的资源，能我们家在皮肤资源什么思路吗？加载外部资源的 Resources 能通过类似的思路吗？<br>我们查看下 Resources 类的源码，发现 Resources 的构造函数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">/**</div><div class="line">     * Create a new Resources object on top of an existing set of assets in an</div><div class="line">     * AssetManager.</div><div class="line">     *</div><div class="line">     * @param assets Previously created AssetManager.</div><div class="line">     * @param metrics Current display metrics to consider when</div><div class="line">     *                selecting/computing resource values.</div><div class="line">     * @param config Desired device configuration to consider when</div><div class="line">     *               selecting/computing resource values (optional).</div><div class="line">     */</div><div class="line">    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) &#123;</div><div class="line">        this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>这里关键是第一个参数如何获取，第二和第三个参数可以通过 Activity 获取到。<br>我们再去看下 AssetManager 的代码，同时会发现下面的这个</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">/**</div><div class="line">     * Add an additional set of assets to the asset manager.  This can be</div><div class="line">     * either a directory or ZIP file.  Not for use by applications.  Returns</div><div class="line">     * the cookie of the added asset, or 0 on failure.</div><div class="line">     * &#123;@hide&#125;</div><div class="line">     */</div><div class="line">    public final int addAssetPath(String path) &#123;</div><div class="line">        synchronized (this) &#123;</div><div class="line">            int res = addAssetPathNative(path);</div><div class="line">            makeStringBlocks(mStringBlocks);</div><div class="line">            return res;</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>AssetManager 可以加载一个zip 格式的压缩包，而 Apk 文件不就是一个 压缩包吗。我们通过反射的方法，拿到 AssetManager，加载 Apk 内部的资源，获取到 Resources 对象，这样再想办法，把 R文件里面保存的ID获取到，这样既可以拿到对应的资源文件了。理论上我们的思路时成立的。<br>我们看下，如何通过代码获取 Resources 对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">AssetManager assetManager = AssetManager.class.newInstance();</div><div class="line">Method addAssetPath = assetManager.getClass().getMethod(&quot;addAssetPath&quot;, String.class);</div><div class="line">addAssetPath.invoke(assetManager, skinPkgPath);</div><div class="line"></div><div class="line">Resources superRes = context.getResources();</div><div class="line">Resources skinResource = new Resources(assetManager,superRes.getDisplayMetrics(),superRes.getConfiguration());</div></pre></td></tr></table></figure>
<h4 id="标记需要换肤的-View"><a href="#标记需要换肤的-View" class="headerlink" title="标记需要换肤的 View"></a>标记需要换肤的 View</h4><p>找到资源文件之后，我们要接着标记需要换肤的 View 。<br><strong>找到需要换肤的 View</strong><br>怎么寻找哪些是我们要关注的 View 呢？ 我们还是重 View 的创建时机寻找机会。我们添加一个布局文件时，会使用 LayoutInflater的 Inflater方法，我们看下这个方法是怎么讲一个View添加到Activity 中的。<br>LayoutInflater 中有个接口<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">public interface Factory &#123;</div><div class="line">       /**</div><div class="line">        * Hook you can supply that is called when inflating from a LayoutInflater.</div><div class="line">        * You can use this to customize the tag names available in your XML</div><div class="line">        * layout files.</div><div class="line">        * </div><div class="line">        * &lt;p&gt;</div><div class="line">        * Note that it is good practice to prefix these custom names with your</div><div class="line">        * package (i.e., com.coolcompany.apps) to avoid conflicts with system</div><div class="line">        * names.</div><div class="line">        * </div><div class="line">        * @param name Tag name to be inflated.</div><div class="line">        * @param context The context the view is being created in.</div><div class="line">        * @param attrs Inflation attributes as specified in XML file.</div><div class="line">        * </div><div class="line">        * @return View Newly created view. Return null for the default</div><div class="line">        *         behavior.</div><div class="line">        */</div><div class="line">       public View onCreateView(String name, Context context, AttributeSet attrs);</div><div class="line">   &#125;</div></pre></td></tr></table></figure></p>
<p>根据这里的注释描述，我们可以自己实现这个接口，在 <strong>onCreateView 方法中选择我们需要标记的View</strong>，根据 AttributeSet 值，过滤不需要关注的View。<br><strong>标记 View 与对应的资源</strong><br>我们在 View 创建时，通过过滤 Attribute 属性，找到我们要标记的 View ，下面我们就把这些View的属性记下来</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">for (int i = 0; i &lt; attrs.getAttributeCount(); i++)&#123;</div><div class="line">            String attrName = attrs.getAttributeName(i);</div><div class="line">            String attrValue = attrs.getAttributeValue(i);</div><div class="line">            if(!AttrFactory.isSupportedAttr(attrName))&#123;</div><div class="line">                continue;</div><div class="line">            &#125;  </div><div class="line">            if(attrValue.startsWith(&quot;@&quot;))&#123;</div><div class="line">                try &#123;</div><div class="line">                    int id = Integer.parseInt(attrValue.substring(1));</div><div class="line">                    String entryName = context.getResources().getResourceEntryName(id);</div><div class="line">                    String typeName = context.getResources().getResourceTypeName(id);</div><div class="line">                    SkinAttr mSkinAttr = AttrFactory.get(attrName, id, entryName, typeName);</div><div class="line">                    if (mSkinAttr != null) &#123;</div><div class="line">                        viewAttrs.add(mSkinAttr);</div><div class="line">                    &#125;</div><div class="line">                &#125; catch (NumberFormatException e) &#123;</div><div class="line">                    e.printStackTrace();</div><div class="line">                &#125; catch (NotFoundException e) &#123;</div><div class="line">                    e.printStackTrace();</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line">        &#125;</div></pre></td></tr></table></figure>
<p>然后把这些 View 和属性值，一起封装保存起来</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">if(!ListUtils.isEmpty(viewAttrs))&#123;</div><div class="line">            SkinItem skinItem = new SkinItem();</div><div class="line">            skinItem.view = view;</div><div class="line">            skinItem.attrs = viewAttrs;</div><div class="line">            mSkinItems.add(skinItem);</div><div class="line">            if(SkinManager.getInstance().isExternalSkin())&#123;</div><div class="line">                skinItem.apply();</div><div class="line">            &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<h4 id="及时更新-UI"><a href="#及时更新-UI" class="headerlink" title="及时更新 UI"></a>及时更新 UI</h4><p>由于我们把需要更新的View 以及属性值都保存起来了，更新的时候只要把他们取出来遍历一遍即可。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">@Override</div><div class="line">    public void onThemeUpdate() &#123;</div><div class="line">        if(!isResponseOnSkinChanging)&#123;</div><div class="line">            return;</div><div class="line">        &#125;</div><div class="line">        mSkinInflaterFactory.applySkin();</div><div class="line">    &#125;</div><div class="line">//applySkin 的具体实现</div><div class="line"></div><div class="line">public void applySkin()&#123;</div><div class="line">        if(ListUtils.isEmpty(mSkinItems))&#123;</div><div class="line">            return;</div><div class="line">        &#125;   </div><div class="line">        for(SkinItem si : mSkinItems)&#123;</div><div class="line">            if(si.view == null)&#123;</div><div class="line">                continue;</div><div class="line">            &#125;</div><div class="line">            si.apply();</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<h4 id="制作皮肤包"><a href="#制作皮肤包" class="headerlink" title="制作皮肤包"></a>制作皮肤包</h4><p>皮肤包制作相对简单<br><strong>1.创建独立 model，包名自定义</strong><br><strong>2.添加资源文件到 model 中,不需要 java 代码</strong><br><strong>3.运行 build.gradle 脚本，生成 xxx.skin 皮肤包</strong></p>
<h4 id="制定一套完整的在线换肤方案"><a href="#制定一套完整的在线换肤方案" class="headerlink" title="制定一套完整的在线换肤方案"></a>制定一套完整的在线换肤方案</h4><p>到这里之后，还是没看到在线换肤方案啊～说好的在线换肤方案呢？<br><strong>1.将制作好的皮肤包上传到服务端后台</strong><br><strong>2.客户端根据接口数据，处理皮肤加载逻辑</strong></p>
<h4 id="模块依赖关系"><a href="#模块依赖关系" class="headerlink" title="模块依赖关系"></a>模块依赖关系</h4><p><img src="http://7xrkqt.com1.z0.glb.clouddn.com/blogimage/2016/QQ20160406-0.png-blogpic" alt="三个模块的依赖关系"></p>
<p>换肤方案的介绍基本完成，下面是一些参考资料和资源</p>
<p><strong><a href="http://7xsnut.com2.z0.glb.clouddn.com/resource/2016/Android%E5%9C%A8%E7%BA%BF%E6%8D%A2%E8%82%A4%E6%96%B9%E6%A1%88%E5%88%86%E4%BA%AB.pptx" target="_blank" rel="external">分享ppt文件</a></strong></p>
<p><strong><a href="https://github.com/imesong/themePlugin" target="_blank" rel="external">Demo源码</a></strong></p>
<p><strong><a href="http://7xsnut.com2.z0.glb.clouddn.com/resource/2016/%E6%8D%A2%E8%82%A4%E6%96%B9%E6%A1%88.xmind" target="_blank" rel="external">参考开源资料xmind文件下载</a></strong></p>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Android/" rel="tag"># Android</a>
          
            <a href="/tags/View/" rel="tag"># View</a>
          
            <a href="/tags/Resources/" rel="tag"># Resources</a>
          
            <a href="/tags/换肤/" rel="tag"># 换肤</a>
          
        </div>
      

      
        
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2016/04/05/2016/Android反编译实战－揭秘猎豹设置默认浏览器实现逻辑/" rel="next" title="Android反编译－揭秘猎豹设置默认浏览器逻辑">
                <i class="fa fa-chevron-left"></i> Android反编译－揭秘猎豹设置默认浏览器逻辑
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2015/12/31/2015summary/" rel="prev" title="我对这个世界仍有期许">
                我对这个世界仍有期许 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          
  <div class="comments" id="comments">
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="https://s.gravatar.com/avatar/bd2525e0ecc6d8022339c7dce27065c0?s=160"
               alt="imesong" />
          <p class="site-author-name" itemprop="name">imesong</p>
           
              <p class="site-description motion-element" itemprop="description"></p>
          
        </div>
        <nav class="site-state motion-element">

          
            <div class="site-state-item site-state-posts">
              <a href="/archives">
                <span class="site-state-item-count">10</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          
            
            
            <div class="site-state-item site-state-categories">
              <a href="/categories/index.html">
                <span class="site-state-item-count">5</span>
                <span class="site-state-item-name">分类</span>
              </a>
            </div>
          

          
            
            
            <div class="site-state-item site-state-tags">
              <a href="/tags/index.html">
                <span class="site-state-item-count">20</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/imesong" target="_blank" title="GitHub">
                  
                    <i class="fa fa-fw fa-github"></i>
                  
                  GitHub
                </a>
              </span>
            
          
        </div>

        
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#为什么要做在线换肤？"><span class="nav-number">1.</span> <span class="nav-text">为什么要做在线换肤？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#皮肤模块独立，减小-Apk-包大小"><span class="nav-number">1.1.</span> <span class="nav-text">皮肤模块独立，减小 Apk 包大小</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#技术方案通用，不局限于具体的-App"><span class="nav-number">1.2.</span> <span class="nav-text">技术方案通用，不局限于具体的 App</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#服务端控制皮肤包，动态更新，满足运营需求"><span class="nav-number">1.3.</span> <span class="nav-text">服务端控制皮肤包，动态更新，满足运营需求</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#降低维护成本"><span class="nav-number">1.4.</span> <span class="nav-text">降低维护成本</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#在线换肤的难点在哪里？"><span class="nav-number">2.</span> <span class="nav-text">在线换肤的难点在哪里？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#如何加载皮肤资源文件"><span class="nav-number">2.1.</span> <span class="nav-text">如何加载皮肤资源文件</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#标记需要换肤的-View"><span class="nav-number">2.2.</span> <span class="nav-text">标记需要换肤的 View</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#及时更新-UI"><span class="nav-number">2.3.</span> <span class="nav-text">及时更新 UI</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#制作皮肤包"><span class="nav-number">2.4.</span> <span class="nav-text">制作皮肤包</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#制定一套完整的在线换肤方案"><span class="nav-number">2.5.</span> <span class="nav-text">制定一套完整的在线换肤方案</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#模块依赖关系"><span class="nav-number">2.6.</span> <span class="nav-text">模块依赖关系</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  &copy; 
  <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">imesong</span>
</div>


<div class="powered-by">
  由 <a class="theme-link" href="https://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Pisces
  </a>
</div>


        

        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script>



  


  




	





  





  





  






  





  

  

  

  

  

</body>
</html>
